package com.example.demo;

import org.apache.dubbo.config.spring.context.annotation.EnableDubbo;
import org.apache.dubbo.rpc.Exporter;
import org.apache.dubbo.rpc.ProtocolServer;
import org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol;
import org.mybatis.spring.annotation.MapperScan;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Collection;

@RestController
@EnableDubbo(scanBasePackages = "com.example.demo.impl")
//@EnableAutoConfiguration
@SpringBootApplication
//@ComponentScan({"com.alibaba.arms.demo"})
@MapperScan({"com.example.demo.dao"})
public class Demo1Application {

    private final static Logger log = LoggerFactory.getLogger(Demo1Application.class) ;

    @RequestMapping
    public String index(){
        return "hello";
    }

    public static void main(String[] args) throws InterruptedException {
//        Java对每个信号都启动一个线程进行处理。注册TERM信号，就启动"SIGTERM handler" 线程。即便主线程被阻塞，信号依然可以得到处理。
//        DemoSignalHandler.listenTo("HUP"); //11 挂起。JVM 正常退出。
//        DemoSignalHandler.listenTo("KILL");

//        DemoSignalHandler.listenTo("INT"); //2 交互式注意信号（CTRL-C）。JVM 正常退出。
//        DemoSignalHandler.listenTo("TERM"); //15 终止请求。JVM 将正常退出。
//        DemoSignalHandler.listenTo("USR2");



//        https://jaskey.github.io/blog/2019/09/30/spring-boot-dubbo-graceful-shutdown/
//        SpringApplication.run(Demo1Application.class, args);


        SpringApplication application = new SpringApplication(Demo1Application.class);
        //关闭spring的shutdown hook，后续手动触发
        application.setRegisterShutdownHook(false);
        final ConfigurableApplicationContext context = application.run(args);
        Runtime.getRuntime().addShutdownHook(new Thread("T_SHUTDOWN_HOOK") {
            @Override
            public void run() {
                log.info("”====================begin shutdown App====================“。");
                //....这里可以做其他优雅退出处理，例如回收本地线程池、关闭定时调度器等的操作
                waitDubboShutdown(1000,5);//每次等1000ms，最多等5次；优雅退出时间是动态的（可能1秒就能优雅退出完毕）；但如果退出时间大于5秒，那么则放弃优雅退出，直接退出。
//                System.out.println("start");
//                log.info("”====================start shutdown App====================“。");
                //关闭spring容器
                context.close();
//                System.out.println("end");
//                log.info("”====================end shutdown App====================“。");
            }
        });


//        Thread.sleep(20000);
    }

    /**
     * 等待Dubbo退出，优雅退出的shutdown hook可使用
     * @param sleepMillis 每次发现Dubbo没退出完就睡眠等待的毫秒数
     * @param sleepMaxTimes 最多睡眠的次数，避免一直dubbo退出太久卡住程序的退出，达到此次数后会不再等待
     */
    public static void waitDubboShutdown(long sleepMillis, int sleepMaxTimes) {
        log.info("”====================waiting dubbo to shutdown====================“。");
        for (int sleepWaitTimes=0; sleepWaitTimes <sleepMaxTimes; sleepWaitTimes++){
            //如果dubbo的server没有关闭完成，会睡眠等待，最多等待三次

//            注：这个方法用到了DubboProtocol的底层API，
//            所以如果你的协议不是使用”dubbo”而是如HTTP协议、redis协议，则此方法不可用。
            Collection<ProtocolServer> existingDubboServers = DubboProtocol.getDubboProtocol().getServers();
            Collection<Exporter<?>> existingDubboExporters  = DubboProtocol.getDubboProtocol().getExporters();
            log.info("existing dubbo servers : {}, existing dubbo expoerters {} ,  sleepWaitTimes : {}", existingDubboServers, existingDubboExporters, sleepWaitTimes);
            if (!existingDubboServers.isEmpty() || !existingDubboExporters.isEmpty()) {
                try {
                    Thread.sleep(sleepMillis);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            } else {
                break;
            }
        }

        //优雅退出失败，打印日志
        Collection<ProtocolServer> existingDubboServers = DubboProtocol.getDubboProtocol().getServers();
        if (!existingDubboServers.isEmpty()) {

            log.warn("DUBBO服务Server依然存在，不再等待其销毁，可能会导致优雅退出失败 {}",existingDubboServers);
        }

        Collection<Exporter<?>> existingDubboExporters  = DubboProtocol.getDubboProtocol().getExporters();
        if (!existingDubboExporters.isEmpty()) {
            log.warn("DUBBO服务Exporters依然存在，不再等待其销毁，可能会导致优雅退出失败 {}",existingDubboExporters);
        }
    }

}
